#ifndef __MEMORY_POOL__
#define __MEMORY_POOL__

#include<unordered_map>
#include<mutex>
#include<memory>

#include<stdlib.h>

#include"chunk.hpp"
#include"../log/log.hpp"

typedef std::shared_ptr<Chunk> Chunkptr;
//可以使用智能指针管理内存块？？
typedef std::unordered_map<int,Chunkptr> pool_t;

typedef enum
{
    mlow = 4096,
    m4K = mlow,
    m16K = m4K * 4,
    m64K = m16K * 4,
    m256K = m64K * 4,
    m1M = m256K * 4,
    m4M = m1M * 4,
    mup = m4M
}MEM_CAP;

#define MAX_POOL_SIZE_KB (4 * 1024 * 1024)

class Memorypool
{
public:
    static Memorypool& get_instance()
    {
        static Memorypool instance;
        return instance;
    }

    //分配内存块
    Chunkptr pop(int n)
    {
        //判断申请的内存块大小是否存在
        int index;
        bool found=false;
        for(index=mlow;index<=mup;index*=4)
        {
            if(n<=index)
            {
                found=true;
                break;
            }
        }
        if(!found)return nullptr;

        //若没有对应内存块，则申请一块返回，若有则取出一块返回
        std::lock_guard<std::mutex> mtx(_mp_mutex);
        if(_mp_pool[index]==nullptr)
        {
            if(_mp_total_size_kb+index/1024>=MAX_POOL_SIZE_KB)
            {
                ERR_LOG("beyond the limit size of memory!\n");
                exit(1);
            }

            auto newbuff = std::make_shared<Chunk>(index);
            if (newbuff == nullptr)
            {
                ERR_LOG("new chunk %d error!\n", static_cast<int>(index));
                exit(1);
            }
            _mp_total_size_kb+=index/1024;
            return newbuff;
        }
        auto newbuff=_mp_pool[index];
        _mp_pool[index]=newbuff->_next;
        newbuff->_next=nullptr;
        _mp_left_size_kb-=index/1024;

        return newbuff;
    }

    Chunkptr pop(){return pop(m4K);}

    //取回内存块
    void push(Chunkptr block)
    {
        int index=block->_capacity;
        block->_length=0;
        block->_head=0;

        std::lock_guard<std::mutex> mtx(_mp_mutex);
        assert(_mp_pool.count(index));

        block->_next=_mp_pool[index];
        _mp_pool[index]=block;
        _mp_left_size_kb+=block->_capacity/1024;
    }

    ~Memorypool()=default;

    //测试接口
    // [[deprecated("mem pool debug api deprecated!")]]
    int get_total_size_kb(){return _mp_total_size_kb;}

    // [[deprecated("mem pool debug api deprecated!")]]
    int get_left_size_kb(){return _mp_left_size_kb;}

    // [[deprecated("mem pool debug api deprecated!")]]
    int get_list_size_byte(MEM_CAP index)
    {
        int size=0;
        std::lock_guard<std::mutex> mtx(_mp_mutex);
        assert(_mp_pool.count(index));
        
        auto node=_mp_pool[index];
        while(node)
        {
            size+=node->_capacity;
            node=node->_next;
        }
        return size;
    }

    // [[deprecated("mem pool debug api deprecated!")]]
    void printf_list_content(MEM_CAP index)
    {
        std::lock_guard<std::mutex> mtx(_mp_mutex);
        int count=0;
        printf("***************start to print %dkb chunk_size list data*******************\n", index/1024);
        assert(_mp_pool.count(index));
        auto node=_mp_pool[index];
        while(node)
        {
            if((count++)<=5)node->test();
            node=node->_next;
        }
        printf("...\n");
        printf("******************end, node cnt is %d************************\n\n", count);
    }

private:
    Memorypool()  
    {
        init(m4K,2000);
        init(m16K,500);
        init(m64K,250);
        init(m256K,100);
        init(m1M,25);
        init(m4M,10);
        _mp_left_size_kb=_mp_total_size_kb;
    }

    Memorypool(const Memorypool&)=delete;

    Memorypool(Memorypool&&)=delete;

    Memorypool& operator=(const Memorypool&)=delete;

    Memorypool& operator=(Memorypool&&)=delete;

    //初始化，预先申请内存空间
    void init(MEM_CAP size,int chunk_num)
    {
        Chunkptr prev;
        //这种做法是错误的，智能指针不支持隐式转换
        // std::shared_ptr<Chunk> ch_ptr=new Chunk(size);
        //需要事先显式构造
        // Chunk* ptr=new Chunk(size);
        // std::shared_ptr<Chunk> ch_ptr(ptr);
        //这种做法最好
        _mp_pool[size]=std::make_shared<Chunk>(size);
        if(_mp_pool[size]==nullptr)
        {
            ERR_LOG("new chunk %d error!\n",static_cast<int>(size));
            exit(1);
        }
        prev=_mp_pool[size];

        for(int i=1;i<chunk_num;i++)
        {
            prev->_next=std::make_shared<Chunk>(size);
            if (prev->_next == nullptr)
            {
                ERR_LOG("new chunk %d error!\n", static_cast<int>(size));
                exit(1);
            }
            prev=prev->_next;
        }
        _mp_total_size_kb+=size/1024 *chunk_num;
    }

private:
    pool_t _mp_pool;                //通过哈希表存储的内存池
    uint64_t _mp_total_size_kb{0};  //内存池现在达到过的最大总量
    uint64_t _mp_left_size_kb{0};   //内存池现在总量
    std::mutex _mp_mutex;
};

#endif